"""
匿名请求闲鱼商品详情的工具（后端版本）
不需要登录，只需要获取 token 后请求商品信息
"""

import hashlib
import json
import time
import ssl
from typing import Dict, Any, Optional
from urllib.parse import quote

import aiohttp
from utils.log_util import logger


class AnonymousGoofishClient:
    """匿名请求闲鱼API的工具类（后端版本）"""
    
    HOST_URL = "https://h5api.m.goofish.com/h5"
    APP_KEY = "34839810"
    
    def __init__(self):
        self.session: Optional[aiohttp.ClientSession] = None
        self.token: Optional[str] = None
    
    async def __aenter__(self):
        """异步上下文管理器入口"""
        # ✅ 创建SSL上下文，禁用证书验证（避免服务器环境SSL证书问题）
        # ⚠️ 注意：在生产环境中，如果可能，应该配置正确的SSL证书
        # 这里禁用验证是为了兼容某些服务器环境（如缺少CA证书的情况）
        ssl_context = ssl.create_default_context()
        ssl_context.check_hostname = False
        ssl_context.verify_mode = ssl.CERT_NONE
        
        # 创建连接器，使用自定义SSL上下文
        connector = aiohttp.TCPConnector(ssl=ssl_context)
        
        # 创建会话，使用自定义连接器
        self.session = aiohttp.ClientSession(connector=connector)
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """异步上下文管理器退出"""
        if self.session:
            await self.session.close()
    
    def get_timestamp(self) -> int:
        """获取时间戳（毫秒）"""
        return int(time.time() * 1000)
    
    def build_sign(self, token: str, timestamp: int, data: str) -> str:
        """
        构建签名
        
        Args:
            token: token值（可以为空字符串）
            timestamp: 时间戳（毫秒）
            data: JSON数据（字符串）
        
        Returns:
            MD5签名
        """
        sign_str = f"{token}&{timestamp}&{self.APP_KEY}&{data}"
        sign = hashlib.md5(sign_str.encode()).hexdigest()
        return sign
    
    async def get_token(self, item_id: int = 991819717397, retry_count: int = 0) -> Optional[str]:
        """
        步骤1：获取 token（通过推荐列表API）
        
        Args:
            item_id: 商品ID（用于推荐列表API）
            retry_count: 重试次数（内部使用）
        
        Returns:
            token 字符串，如果失败返回 None
        """
        try:
            # 构建请求参数
            request_data_dict = {
                "itemId": item_id,
                "categoryId": "",
                "pageNum": 1,
                "pageSize": 30,
                "reqFrom": "xianyuweb"
            }
            request_data = json.dumps(request_data_dict, ensure_ascii=False)
            timestamp = self.get_timestamp()
            
            # 使用空 token 构建签名（首次请求）
            sign = self.build_sign("", timestamp, request_data)
            
            # 构建 URL
            api_name = "mtop.taobao.idle.item.web.recommend.list"
            url = f"{self.HOST_URL}/{api_name}/1.0/?"
            url_params = [
                "jsv=2.7.2",
                f"appKey={self.APP_KEY}",
                f"t={timestamp}",
                f"sign={sign}",
                "v=1.0",
                "type=originaljson",
                "accountSite=xianyu",
                "dataType=json",
                "timeout=20000",
                f"api={api_name}",
                "sessionOption=AutoLoginOnly",
                "spm_cnt=a21ybx.item.0.0"
            ]
            url += "&".join(url_params)
            
            # 构建请求头（模拟真实浏览器）
            headers = {
                "accept": "application/json",
                "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
                "content-type": "application/x-www-form-urlencoded",
                "origin": "https://www.goofish.com",
                "priority": "u=1, i",
                "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
                "sec-ch-ua-mobile": "?0",
                "sec-ch-ua-platform": '"Windows"',
                "sec-fetch-dest": "empty",
                "sec-fetch-mode": "cors",
                "sec-fetch-site": "same-site",
                "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
                "referer": "https://www.goofish.com/"
            }
            
            # 发送请求（不设置 Cookie 头，让服务器自动返回）
            post_data = f"data={quote(request_data)}"
            async with self.session.post(url, data=post_data, headers=headers, timeout=10) as response:
                response_text = await response.text()
                result = json.loads(response_text)
                
                # 检查响应
                ret_codes = result.get("ret", [])
                
                # ⚠️ 注意：即使返回 FAIL_SYS_TOKEN_EMPTY，服务器也可能返回了 token cookie
                # 先尝试从 cookie_jar 中提取 token
                cookies = {}
                for cookie in self.session.cookie_jar:
                    cookies[cookie.key] = cookie.value
                
                # 获取 _m_h5_tk
                token_full = cookies.get("_m_h5_tk", "")
                if token_full:
                    token = token_full.split('_')[0] if '_' in token_full else token_full
                    self.token = token
                    logger.debug(f"Token获取成功: {token[:30]}...")
                    # ✅ 记录所有cookie以便调试
                    logger.debug(f"获取到的所有cookie: {list(cookies.keys())}")
                    return token
                
                # 如果响应成功，但没获取到 token，可能是其他问题
                if ret_codes and any(code.startswith("SUCCESS") for code in ret_codes):
                    logger.warning("响应成功但未获取到token cookie")
                    return None
                else:
                    # 即使失败，也记录错误码，但可能 token 已经在 cookie 中了
                    logger.debug(f"Token请求返回: {ret_codes}, 但已检查cookie中的token")
                    return None
        except aiohttp.ClientConnectorCertificateError as e:
            logger.error(f"获取token时SSL证书验证失败: {e}")
            logger.error("这可能是服务器环境缺少SSL证书或证书配置问题")
            return None
        except Exception as e:
            logger.exception(f"获取token时出错: {e}")
            return None
    
    async def get_item_detail(self, item_id: int, retry_count: int = 0) -> Optional[Dict[str, Any]]:
        """
        步骤2：获取商品详情
        
        Args:
            item_id: 商品ID
            retry_count: 重试次数（内部使用）
        
        Returns:
            商品详情数据，如果失败返回 None
        """
        max_retries = 2  # 最大重试次数
        try:
            # 如果还没有 token，先获取
            if not self.token:
                token = await self.get_token(item_id)
                if not token:
                    logger.error("无法获取token，无法请求商品详情")
                    return None
            
            # 构建请求参数
            request_data_dict = {
                "itemId": str(item_id)
            }
            request_data = json.dumps(request_data_dict, ensure_ascii=False)
            timestamp = self.get_timestamp()
            
            # 使用 token 构建签名
            sign = self.build_sign(self.token, timestamp, request_data)
            
            # 构建 URL
            api_name = "mtop.taobao.idle.pc.detail"
            url = f"{self.HOST_URL}/{api_name}/1.0/?"
            url_params = [
                "jsv=2.7.2",
                f"appKey={self.APP_KEY}",
                f"t={timestamp}",
                f"sign={sign}",
                "v=1.0",
                "type=originaljson",
                "accountSite=xianyu",
                "dataType=json",
                "timeout=20000",
                f"api={api_name}",
                "sessionOption=AutoLoginOnly",
                "spm_cnt=a21ybx.item.0.0"
            ]
            url += "&".join(url_params)
            
            # 构建请求头（模拟真实浏览器）
            headers = {
                "accept": "application/json",
                "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
                "content-type": "application/x-www-form-urlencoded",
                "origin": "https://www.goofish.com",
                "priority": "u=1, i",
                "sec-ch-ua": '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
                "sec-ch-ua-mobile": "?0",
                "sec-ch-ua-platform": '"Windows"',
                "sec-fetch-dest": "empty",
                "sec-fetch-mode": "cors",
                "sec-fetch-site": "same-site",
                "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
                "referer": "https://www.goofish.com/"
            }
            
            # ✅ 构建 Cookie 头（包含之前获取的 token）
            # ⚠️ 注意：token应该从cookie_jar中获取，而不是从self.token属性
            cookie_parts = []
            for cookie in self.session.cookie_jar:
                cookie_parts.append(f"{cookie.key}={cookie.value}")
            
            # ✅ 如果token存在但没有在cookie中，手动添加到Cookie头
            # 注意：_m_h5_tk的格式是 "{token}_{timestamp}"
            has_m_h5_tk = any("_m_h5_tk" in c for c in cookie_parts)
            if self.token and not has_m_h5_tk:
                # token需要包含在cookie中，格式：_m_h5_tk={token}_{timestamp}
                timestamp_ms = int(time.time() * 1000)
                cookie_parts.append(f"_m_h5_tk={self.token}_{timestamp_ms}")
                logger.debug(f"手动添加_m_h5_tk到Cookie头: {self.token[:20]}...")
            
            if cookie_parts:
                headers["Cookie"] = "; ".join(cookie_parts)
                logger.debug(f"Cookie头已设置，包含 {len(cookie_parts)} 个cookie")
            else:
                logger.warning("Cookie头为空，可能导致token验证失败")
            
            # 发送请求
            post_data = f"data={quote(request_data)}"
            async with self.session.post(url, data=post_data, headers=headers, timeout=10) as response:
                response_text = await response.text()
                result = json.loads(response_text)
                
                # 检查响应
                ret_codes = result.get("ret", [])
                if ret_codes and any(code.startswith("SUCCESS") for code in ret_codes):
                    logger.debug(f"商品详情获取成功: itemId={item_id}")
                    return result.get("data", {})
                else:
                    # ✅ 检查是否是风控错误
                    error_msg = result.get('msg', 'N/A')
                    error_str = str(ret_codes)
                    
                    # 风控错误：RGV587_ERROR::SM（被挤爆啦）
                    if 'RGV587_ERROR' in error_str or '被挤爆啦' in error_msg or '请稍后重试' in error_msg:
                        if retry_count < max_retries:
                            logger.warning(f"遇到风控，重试获取商品详情（{retry_count + 1}/{max_retries}）: itemId={item_id}")
                            # 清空当前session，重新获取token
                            await self.session.close()
                            self.session = None
                            self.token = None
                            # 重新初始化session
                            ssl_context = ssl.create_default_context()
                            ssl_context.check_hostname = False
                            ssl_context.verify_mode = ssl.CERT_NONE
                            connector = aiohttp.TCPConnector(ssl=ssl_context)
                            self.session = aiohttp.ClientSession(connector=connector)
                            # 递归重试
                            return await self.get_item_detail(item_id, retry_count + 1)
                        else:
                            logger.error(f"商品详情获取失败（风控，已重试{max_retries}次）: {ret_codes}, msg={error_msg}")
                            return None
                    # Token相关错误
                    elif 'TOKEN' in error_str.upper() or '令牌' in error_msg:
                        if retry_count < max_retries:
                            logger.warning(f"Token问题，重新获取token并重试（{retry_count + 1}/{max_retries}）: itemId={item_id}")
                            # 重新获取token
                            new_token = await self.get_token(item_id, retry_count)
                            if new_token:
                                self.token = new_token
                                return await self.get_item_detail(item_id, retry_count + 1)
                        logger.error(f"商品详情获取失败（token问题）: {ret_codes}, msg={error_msg}")
                        logger.debug(f"当前token: {self.token[:20] if self.token else 'None'}...")
                        logger.debug(f"Cookie头: {headers.get('Cookie', '')[:100]}...")
                        return None
                    else:
                        logger.error(f"商品详情获取失败: {ret_codes}, msg={error_msg}")
                        return None
        except aiohttp.ClientConnectorCertificateError as e:
            logger.error(f"获取商品详情时SSL证书验证失败: {e}")
            logger.error("这可能是服务器环境缺少SSL证书或证书配置问题")
            return None
        except Exception as e:
            logger.exception(f"获取商品详情时出错: {e}")
            return None

